package it.unibo.refolding.pico.stripchart.ui;

import it.unibo.refolding.common.CommonThings;
import it.unibo.refolding.pico.ApproachRetractionLoop;
import it.unibo.refolding.pico.PicoData;
import it.unibo.refolding.pico.PicoDataExtractor;
import it.unibo.refolding.pico.PicoShapes;
import it.unibo.refolding.pico.PicoUtil;
import it.unibo.refolding.pico.StripChartPicoData;
import it.unibo.refolding.pico.stripchart.AnalysisOptions;
import it.unibo.refolding.pico.stripchart.FetchRefoldBreakParams;
import it.unibo.refolding.pico.stripchart.JpHelper;
import it.unibo.refolding.pico.ui.UiUtil;
import it.unimi.dsi.fastutil.doubles.DoubleList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.shorts.ShortList;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JRootPane;
import javax.swing.JScrollPane;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.Parser;
import org.apache.commons.lang3.StringUtils;

import com.google.common.base.Preconditions;

public class JunctionPointsMain {

	private static final Logger logger = Logger.getLogger(JunctionPointsMain.class.getSimpleName());

	private static JInternalFrame createFrame(final JunctionPointsHolder h) {
		JInternalFrame internalFrame = new JInternalFrame(h.filename(), true, true, true, true);
		internalFrame.setVisible(true);
		return internalFrame;
	}

	protected void quit() {
		System.exit(0);
	}

	/**
	 * Create the GUI and show it. For thread safety, this method should be
	 * invoked from the event-dispatching thread.
	 * 
	 * @param hs
	 */
	private static void createAndShowGUI(List<JunctionPointsHolder> hs) {
		JFrame frame = new JFrame();
    frame.setUndecorated(false);
    frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
		int inset = 50;
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		frame.setBounds(inset, inset, screenSize.width - inset * 2, screenSize.height - inset * 2);
		JDesktopPane desktop = new JDesktopPane();
		for (int i = 0; i < hs.size(); ++i) {
			JunctionPointsHolder h = hs.get(i);
			JInternalFrame internalFrame = createFrame(h);
			internalFrame.setLocation(i, i);
			internalFrame.setSize(800, 600);
			List<Color> segmentColors = PicoShapes.colorsForPieces(1 + h.modifiableJunctionPoints.size());
			ZtPanel panel = new ZtPanel(h, segmentColors);
			ZtListener listener = new ZtListener(panel);
			panel.addMouseListener(listener);
			panel.addMouseMotionListener(listener);
			UiUtil.addKeyShortcutForClosing(internalFrame, panel);
			final JScrollPane scrollPane = new JScrollPane(panel);
			scrollPane.getHorizontalScrollBar().setUnitIncrement(16);
			scrollPane.getVerticalScrollBar().setUnitIncrement(16);
			int extra = 50;
			int iterations = (h.modifiableJunctionPoints.size() + 1) / (ApproachRetractionLoop.APPROACH_RETRACTION_SEGMENTS + 1);
			if (iterations != 0) {
        int k = (iterations - 1) * (ApproachRetractionLoop.APPROACH_RETRACTION_SEGMENTS + 1);
        // Scroll to the last close-to-surface piece.
        Rectangle rect = new Rectangle(
          h.modifiableJunctionPoints.getInt(k + 1) - extra,
          panel.y(h.modifiableJunctionPoints.getInt(k + 1)) - extra,
          h.modifiableJunctionPoints.getInt(k + 2) + 1 - h.modifiableJunctionPoints.getInt(k + 1)
            + 2 * extra, panel.y(h.modifiableJunctionPoints.getInt(k + 2)) + 1
            - panel.y(h.modifiableJunctionPoints.getInt(k + 1)) + 2 * extra);
  			panel.scrollRectToVisible(rect);
			}
			internalFrame.getContentPane().add(scrollPane);
			desktop.add(internalFrame);
		}
		frame.setContentPane(desktop);
		// Make dragging a little faster but perhaps uglier.
		desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
		frame.setVisible(true);
	}

	public static void main(String[] args) throws ParseException, IllegalArgumentException,
			SecurityException, IllegalAccessException, NoSuchFieldException, IOException {
		Options options = new Options();

		Option dataDirOption = new Option("dataDir", true, "directory with stripchart files");
		dataDirOption.setRequired(true);
		dataDirOption.setType(String.class);
		options.addOption(dataDirOption);
		
    Option inputPathListOption = PicoUtil.createInputPathListOption();
    options.addOption(inputPathListOption);

		Option wildcardPatternOption = new Option("wildcardPattern", true,
				"wildcardPattern for files");
		wildcardPatternOption.setRequired(true);
		wildcardPatternOption.setType(String.class);
		options.addOption(wildcardPatternOption);

		Option guiOption = new Option("gui", true, "show gui?");
		guiOption.setRequired(true);
		guiOption.setType(Boolean.TYPE);
		options.addOption(guiOption);

		Option junctionPointsDirOption = JpHelper.createJunctionPointsOption();
		options.addOption(junctionPointsDirOption);

		Option paramsOption = new Option("params", true,
				"Public static fields from FetchRefoldBreakParams.");
		paramsOption.setRequired(true);
		paramsOption.setType(String.class);
		options.addOption(paramsOption);
		
    Option allowedTimeRelativeErrorOption = AnalysisOptions.createAllowedTimeRelativeErrorOption();
    options.addOption(allowedTimeRelativeErrorOption);

		Parser parser = new GnuParser();
		CommandLine line = parser.parse(options, args);
    String dataDir = line.getOptionValue(dataDirOption.getOpt());
    String inputPathList = line.getOptionValue(inputPathListOption.getOpt());
    Preconditions.checkArgument(
      StringUtils.isEmpty(dataDir) ^ StringUtils.isEmpty(inputPathList),
      "Must specify exactly one of dataDir and inputPathList.");
		String wildcardPattern = line.getOptionValue(wildcardPatternOption.getOpt());
		String junctionPointsDir = line.getOptionValue(junctionPointsDirOption.getOpt());
		boolean gui = Boolean.parseBoolean(line.getOptionValue(guiOption.getOpt()));

    List<Path> files = PicoUtil.computeFileList(dataDir, inputPathList, wildcardPattern);
		Path junctionPointsDirectory = Paths.get(junctionPointsDir);
		CommonThings.createDirectoryPathIfNeeded(junctionPointsDirectory);

		String params = line.getOptionValue(paramsOption.getOpt());
		FetchRefoldBreakParams frbParams = StringUtils.isEmpty(params) ? null
				: FetchRefoldBreakParams.parseOrFail(params);
    DoubleList allowedTimeRelativeError = AnalysisOptions.allowedTimeRelativeError(line, allowedTimeRelativeErrorOption);
		final List<JunctionPointsHolder> hs = new ArrayList<>();
		for (Path file : files) {
			byte[] fileContent = Files.readAllBytes(file);
			PicoData picoData = PicoDataExtractor.parse(fileContent);
			if (!(picoData instanceof StripChartPicoData)) {
				logger.log(Level.WARNING, "Not a strip chart file: {0}", file);
				continue;
			}
			StripChartPicoData stripChart = (StripChartPicoData) picoData;
			Path junctionPointsFile = JpHelper.junctionPointsFile(junctionPointsDirectory, file);
			IntList junctionPoints = JpHelper.readOrComputeAndWriteJunctionPoints(frbParams,
					stripChart, allowedTimeRelativeError, junctionPointsFile);
			ShortList z = stripChart.sharedApproach().sharedZList();
			JunctionPointsHolder h = new JunctionPointsHolder(junctionPointsFile, z, junctionPoints);
			hs.add(h);
		}
		logger.log(Level.INFO, "hs.size()={0}", hs.size());
		if (!hs.isEmpty() & gui) {
			javax.swing.SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					createAndShowGUI(hs);
				}
			});
		}
	}
}
